package library.http;

import android.text.TextUtils;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import library.http.progress.HttpUploadListener;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okio.ByteString;

public class FilePart<T> {
    private static final String UNKNOWN = "unknown";
    private final List<FilePartWrapper> list;
    private FilePartWrapper last;
    private String paramName;

    public void add2MultiBody(MultipartBody.Builder multiBuilder, String name) {
        for (FilePartWrapper wrapper : list) {
            multiBuilder.addFormDataPart(wrapper.getParamName() != null ? wrapper.getParamName() : name, wrapper.fileName(), wrapper.body());
        }

    }

    private FilePart() {
        list = new ArrayList<>();
    }

    private FilePart(int capacity) {
        list = new ArrayList<>(capacity);
    }

    public static FilePart<String> create() {
        return new FilePart<>();
    }

    public static FilePart<String> create(int capacity) {
        return new FilePart<>(capacity);
    }


    public FilePart<String> param(String paramName) {
        this.paramName = paramName;
        return (FilePart<String>) this;
    }

    //path
    public FilePart<String> path(String path) {
        return path(null, path, "");
    }

    public FilePart<String> path(String fileName, String path) {
        return path(fileName, path, "");
    }

    public FilePart<String> path(String fileName, String path, String mediaType) {
        return path(fileName, path, MediaType.parse(mediaType));
    }

    public FilePart<String> path(String fileName, String path, MediaType mediaType) {
        File file = new File(path);
        fileName = !TextUtils.isEmpty(fileName) ? fileName : (file.exists() ? file.getName() : UNKNOWN);
        list.add(last = FilePartWrapper.create(fileName, path, file, mediaType).paramName(paramName));
        return (FilePart<String>) this;
    }

    //file
    public FilePart<File> file(File file) {
        return file(null, file, "");
    }

    public FilePart<File> file(String fileName, File file) {
        return file(fileName, file, "");
    }

    public FilePart<File> file(String fileName, File file, String mediaType) {
        return file(fileName, file, MediaType.parse(mediaType));
    }

    public FilePart<File> file(String fileName, File file, MediaType mediaType) {
        fileName = !TextUtils.isEmpty(fileName) ? fileName : (file.exists() ? file.getName() : UNKNOWN);
        list.add(last = FilePartWrapper.create(fileName, file, file, mediaType).paramName(paramName));
        return (FilePart<File>) this;
    }

    //String - not file path
    public FilePart<String> file(String content) {
        return file(null, content, "");
    }

    public FilePart<String> file(String fileName, String content) {
        return file(fileName, content, "");
    }

    public FilePart<String> file(String fileName, String content, String mediaType) {
        return file(fileName, content, MediaType.parse(mediaType));
    }

    public FilePart<String> file(String fileName, String content, MediaType mediaType) {
        fileName = !TextUtils.isEmpty(fileName) ? fileName : UNKNOWN;
        list.add(last = FilePartWrapper.create(fileName, content, content, mediaType).paramName(paramName));
        return (FilePart<String>) this;
    }

    //ByteString
    public FilePart<ByteString> file(ByteString file) {
        return file(null, file, "");
    }

    public FilePart<ByteString> file(String fileName, ByteString file) {
        return file(fileName, file, "");
    }

    public FilePart<ByteString> file(String fileName, ByteString file, String mediaType) {
        return file(fileName, file, MediaType.parse(mediaType));
    }

    public FilePart<ByteString> file(String fileName, ByteString file, MediaType mediaType) {
        fileName = !TextUtils.isEmpty(fileName) ? fileName : UNKNOWN;
        list.add(last = FilePartWrapper.create(fileName, file, mediaType).paramName(paramName));
        return (FilePart<ByteString>) this;
    }

    //byte[]
    public FilePart<byte[]> file(byte[] file) {
        return file(null, file, "");
    }

    public FilePart<byte[]> file(String fileName, byte[] file) {
        return file(fileName, file, "");
    }

    public FilePart<byte[]> file(String fileName, byte[] file, String mediaType) {
        return file(fileName, file, MediaType.parse(mediaType));
    }

    public FilePart<byte[]> file(String fileName, byte[] file, MediaType mediaType) {
        fileName = !TextUtils.isEmpty(fileName) ? fileName : UNKNOWN;
        list.add(last = FilePartWrapper.create(fileName, file, mediaType).paramName(paramName));
        return (FilePart<byte[]>) this;
    }

    //byte[] offset count
    public FilePart<byte[]> file(byte[] file, int offset, int byteCount) {
        return file(null, file, "", offset, byteCount);
    }

    public FilePart<byte[]> file(String fileName, byte[] file, int offset, int byteCount) {
        return file(fileName, file, "", offset, byteCount);
    }

    public FilePart<byte[]> file(String fileName, byte[] file, String mediaType, int offset, int byteCount) {
        return file(fileName, file, MediaType.parse(mediaType), offset, byteCount);
    }

    public FilePart<byte[]> file(String fileName, byte[] file, MediaType mediaType, int offset, int byteCount) {
        fileName = !TextUtils.isEmpty(fileName) ? fileName : UNKNOWN;
        list.add(last = FilePartWrapper.create(fileName, file, mediaType, offset, byteCount).paramName(paramName));
        return (FilePart<byte[]>) this;
    }

    public FilePart<T> progress(HttpUploadListener<T> progress) {
        return progress(progress, 0, 0);
    }

    /**
     * @param progress
     * @param rateProgress 监测进度时间间隔
     * @param rateSpeed    计算速率时间间隔,影响速率值，间隔越大速率值越平滑
     * @return
     */
    public FilePart<T> progress(HttpUploadListener<T> progress, long rateProgress, long rateSpeed) {
        if (last != null) {
            last.onProgress(progress, rateProgress, rateSpeed);
            last = null;
        } else {
            throw new RuntimeException(String.format("progress()的调用必须出现在file()后面"));
        }
        return this;
    }
}
